﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using com.yoozoo.gta.Extension;
using Modules.Chat;
using UnityEngine;
using UnityEngine.UI;
using Yoozoo.Core.Extensions.Engine;
using Yoozoo.Extension;
using Yoozoo.Gameplay.YGUICustom;
using Yoozoo.Mars.Got;
using Yoozoo.UI.YGUI;

namespace Yoozoo.Gameplay.YGUICustom
{
    public class YEmojiText : YText
    {
        public struct EmojiData
        {
            public Sprite sprite;
            public Vector2 size;
            public bool isCenter;
            public int index;
            public int length;
        }

        public char tagStartChar = '[';
        public char tagEndChar = ']';
        public char tagSplitChar = ',';

        public string OriginalText { get; private set; }

        private readonly StringBuilder sb = new StringBuilder();

        private readonly List<EmojiData> spriteInfoList = new List<EmojiData>();
        private List<EmojiData> temp_spriteInfoList = new List<EmojiData>();

        private const float SpriteCenterScale = 1f;

        public static void SelfRefreshTextWithEllipsis(YEmojiText t, string ellipsis = null, int maxLenght = -1)
        {
            if (string.IsNullOrEmpty(ellipsis))
                ellipsis = "...";

            if (t == null || string.IsNullOrEmpty(t.OriginalText))
            {
                UnityEngine.Debug.LogWarning("YEmojiText.SelfRefreshTextWithEllipsis: t is null or text is empty.");
                return;
            }  
                
            bool isEndInEllipsis = t.OriginalText.EndsWith(ellipsis);
            
            Font myFont = t.font; //chatText is my Text component
            myFont.RequestCharactersInTexture(ellipsis, t.fontSize, t.fontStyle);
            myFont.RequestCharactersInTexture(t.OriginalText, t.fontSize, t.fontStyle);
            CharacterInfo characterInfo = new CharacterInfo();

            var arr = ellipsis.ToCharArray();
            float ellipsisLen = 0;
            int ellipsisCount = arr.Length;
            for (var i = 0; i < arr.Length; i++)
            {
                myFont.GetCharacterInfo(arr[i], out characterInfo, t.fontSize);
                ellipsisLen += characterInfo.advance;
            }

            float max = t.GetComponent<RectTransform>().rect.size.x;
            if (maxLenght > 0)
            {
                max = maxLenght;
            }
            float len = 0;

            arr = t.OriginalText.ToCharArray();
            var sb = new StringBuilder(arr.Length);
            var colorSB = new StringBuilder(64);

            Match rangeMatch = null;
            Vector2Int contentRange = Vector2Int.zero;

            // 统计Emoji
            var emojiDict = new Dictionary<int, int>();
            foreach (var info in t.spriteInfoList)
                emojiDict.Add(info.index, info.length);

            var emojiLen = t.GetEmojiSize();
            int textCount = arr.Length;
            int notEllipsis = textCount - ellipsisCount;
            for (int i = 0; i < arr.Length; i++)
            {
                int indexLen;
                if (emojiDict.TryGetValue(i, out indexLen))
                {
                    len += emojiLen;
                    if (len + ellipsisLen > max)
                    {
                        //判定一下是否是已经处理过的
                        if (!(isEndInEllipsis && i > notEllipsis))
                        {
                            sb.Append(ellipsis);
                            break;
                        }
                    }

                    sb.Append(t.OriginalText.Substring(i, indexLen));
                    i = i + indexLen - 1;
                    continue;
                }

                if (InMatch(rangeMatch, i) == 1 && colorSB.Length > 0)
                {
                    var matches = RichColorBody.Matches(t.OriginalText, rangeMatch.Index);
                    sb.Append(matches[0].Value);
                    sb.Append(colorSB);
                    sb.Append(matches[1].Value);
                    colorSB.Clear();
                    rangeMatch = null;
                    continue;
                }

                if (arr[i] == '<' && InMatch(rangeMatch, i) != 0)
                {
                    rangeMatch = RichColorRange.Match(t.OriginalText, i);
                    contentRange = Vector2Int.zero;
                }

                if (InMatch(rangeMatch, i) != 0)
                {
                    myFont.GetCharacterInfo(arr[i], out characterInfo, t.fontSize);
                    len += characterInfo.advance;
                    //空格字符计算不准
                    if (arr[i] == ' ')
                    {
                        len += 0.2f;
                    }
                    //字符加后缀超出文本框长度或者有换行符提前结束
                    if (len + ellipsisLen > max || arr[i] == '\n')
                    {
                        //判定一下是否是已经处理过的
                        if (!(isEndInEllipsis && i > notEllipsis))
                        {
                            sb.Append(ellipsis);
                            break;
                        }
                    }

                    sb.Append(arr[i]);
                    continue;
                }

                if (contentRange == Vector2Int.zero)
                {
                    var match = RichColorContent.Match(t.OriginalText, i);
                    contentRange = new Vector2Int(match.Index + 1, match.Index + match.Length - 2);
                }

                if (i < contentRange.x || i > contentRange.y)
                    continue;

                myFont.GetCharacterInfo(arr[i], out characterInfo, t.fontSize);
                len += characterInfo.advance;
                if (len + ellipsisLen > max)
                {
                    if (colorSB.Length > 0)
                    {
                        var matches = RichColorBody.Matches(t.OriginalText, rangeMatch.Index);
                        sb.Append(matches[0].Value);
                        sb.Append(colorSB);
                        sb.Append(matches[1].Value);
                    }

                    //判定一下是否是已经处理过的
                    if (!(isEndInEllipsis && i > notEllipsis))
                    {
                        sb.Append(ellipsis);
                        break;
                    }
                }

                colorSB.Append(arr[i]);
            }

            t.text = sb.ToString();
        }

        protected override void Start()
        {
            base.Start();

            if (this.horizontalOverflow != HorizontalWrapMode.Wrap)
            {
                this.horizontalOverflow = HorizontalWrapMode.Wrap;
                Debug.LogWarning("YEmojiText needs set [horizontalOverflow] to [Wrap].");
            }


            if (this.verticalOverflow != VerticalWrapMode.Overflow)
            {
                this.verticalOverflow = VerticalWrapMode.Overflow;
                Debug.LogWarning
                (
                    "YEmojiText needs set [verticalOverflow] to [Overflow]." +
                    "This can block <quad/> height caculation error in some cases. "
                );
            }
        }

        public override string text
        {
            get { return base.text; }
            set
            {
                try
                {
                    var pt = SetTextProText(value);
                    base.text = pt;
                }catch (Exception e)
                {
                    Debug.LogError($"YEmojiText --> text set error {e}  text =  << {value} >>");
                    base.text = "  ";
                }
            }
        }

        /// <summary>
        /// Add Image to <quad/> pos in text.
        /// </summary>
        public void AddImage(EmojiData emojiData)
        {
            this.spriteInfoList.Add(emojiData);

            var spriteCount = this.spriteInfoList.Count;
            YImage image;

            if (spriteCount <= this.transform.childCount)
            {
                var go = this.transform.GetChild(spriteCount - 1).gameObject;
                go.SetActive(true);

                image = go.GetComponent<YImage>();
                image.sprite = null;
            }
            else
            {
                image = this.gameObject.AddChild<YImage>();
            }

            image.name = "Img-" + emojiData.sprite.name;
            image.sprite = emojiData.sprite;
            image.rectTransform.sizeDelta = GetEmojiImageSize();
            image.rectTransform.SetScaleXY(1.0f);
            image.raycastTarget = false;
        }

        /// <summary>
        /// Add Images to each <quad/> pos in text. 
        /// </summary>
        public void AddImages(IEnumerable<EmojiData> SpriteInfos)
        {
            var enumerator = SpriteInfos.GetEnumerator();

            while (enumerator.MoveNext())
            {
                this.AddImage(enumerator.Current);
            }

            enumerator.Dispose();
        }

        /// <summary>
        /// Clear all Images and SpriteInfos.
        /// </summary>
        public void ClearImages()
        {
            this.spriteInfoList.Clear();

            var childCount = this.transform.childCount;

            for (var i = 0; i < childCount; ++i)
            {
                this.transform.GetChild(i).gameObject.SetActive(false);
            }
        }

        protected override void OnPopulateMesh(VertexHelper toFill)
        {
            base.OnPopulateMesh(toFill);

            var count = this.spriteInfoList.Count;

            // display sprites at <quad/> position 
            if (count > 0 && toFill.currentVertCount > 2)
            {
                var vertexLastIndex = toFill.currentVertCount - 1;
                var spriteLastIndex = count - 1;

                var lb = new UIVertex();
                var rt = new UIVertex();

                var t = new Dictionary<int, bool>();

                var offset = GetOffsetY();
                for (var i = spriteLastIndex; i > -1; --i)
                {
                    var index = vertexLastIndex - (spriteLastIndex - i) * 4;
                    toFill.PopulateUIVertex(ref lb, index); // LB
                    toFill.PopulateUIVertex(ref rt, index - 2);
                    
                    t.Add(index, true);
                    for (int j = 0; j < 3; j++)
                    {
                        index -= 1;
                        t.Add(index, true);
                        toFill.SetUIVertex(lb, index);
                    }
                    
                    var p = (lb.position + rt.position) / 2;
                    p.y -= offset;
                    this.transform.GetChild(i).GetComponent<RectTransform>().SetLocalPositionXY(p);
                }

                for (int i = 0; i < vertexLastIndex; i++)
                {
                    if (!t.ContainsKey(i))
                    {
                        toFill.PopulateUIVertex(ref lb, i);
                        lb.position.y += offset;
                        toFill.SetUIVertex(lb, i);
                    }
                }
            }
            else
            {
                ClearImages();
            }
        }

        public String SetTextProText(string text)
        {
            var len = text.Length;
            var pos = -1;
            var last = -1;
            for (var i = 0; i < len; ++i)
            {
                var c = text[i];

                if (c == tagStartChar)
                {
                    pos = i;
                }
                else if (c == tagEndChar && pos != -1)
                {
                    this.sb.Append(text.Substring(last + 1, pos - last - 1));
                    var values = text.Substring(pos + 1, i - pos - 1).Split(this.tagSplitChar);
                    var sprite = EmojiCreator.GetInstance().GetEmoji(values[0].Trim());
                    // handle Sprite
                    if (sprite) //是否匹配到了对应的精灵
                    {
                        var size = GetEmojiSize() * Vector2.one;
                        //this.fontSize / (float) this.baseFontSize
                        //加上上次结束至这次开头的那段字符串
                        this.sb.Append(
                            $"<quad material=1 size={size.y:0000} width={size.x / size.y:0.000} />"); //加载sprite占位符

                        var spriteInfo = new EmojiData
                        {
                            sprite = sprite,
                            size = size,
                            index = pos,
                            length = i - pos + 1
                        };

                        // record the end pos of last tag 
                        this.temp_spriteInfoList.Add(spriteInfo);
                    }
                    else
                    {
                        this.sb.Append(text.Substring(pos, i - pos + 1));
                    }

                    pos = -1;

                    last = i;
                }
            }

            this.OriginalText = text;
            // clear pre images
            this.ClearImages();
            // text has tags
            if (this.sb.Length > 0)
            {
                this.sb.Append(text.Substring(last + 1, len - 1 - last));
                //更新text
                text = this.sb.ToString();
                this.sb.Clear();

                this.AddImages(this.temp_spriteInfoList);
                this.temp_spriteInfoList.Clear();
            }

            return text;
        }

        private int GetEmojiSize()
        {
            return Mathf.CeilToInt(this.style.fontSize * 1.2f);
        }

        private float GetOffsetY()
        {
            return this.style.fontSize * 0.1f;
        }

        private Vector2 GetEmojiImageSize()
        {
            int size = Mathf.CeilToInt(this.style.fontSize * 1.45f);
            return new Vector2(size, size);
        }
    }
}